﻿using DoNet.Common.Helpers;
using Polly;
using System.Collections.Generic;
using System.IO;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace DoNet.ApiHttpClient
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class BaseHttpClient : IHttpClient
    {
        /// <summary>
        /// 接口域名列表
        /// 第一个为通用域名，其他为容灾域名
        /// </summary>
        public virtual Dictionary<string, string> ApiDomains { set; get; } = new Dictionary<string, string>();

        /// <summary>
        /// HttpClient 名称
        /// </summary>
        public virtual string HttpClientName { set; get; } = string.Empty;

        /// <summary>
        /// 
        /// </summary>
        private IHttpClientFactory httpClientFactory;

        /// <summary>
        /// HttpClient
        /// </summary>
        public virtual HttpClient httpClient { set; get; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_httpClientFactory"></param>
        /// <param name="index">域名列表，一般包括所有域名，不传此值时就是自动使用域名，那个域名可以访问就用哪一个，（第一个为通用域名，其他为容灾域名）</param>
        public BaseHttpClient(IHttpClientFactory _httpClientFactory, bool isHttps, int index = -1)
        {
            httpClientFactory = _httpClientFactory;

            int indexCurrent = 0; // 索引基于0开始
            if (ApiDomains != null)
            {
                foreach (var api in ApiDomains)
                {
                    if (index == -1 || (index > -1 && index == indexCurrent))
                    {
                        var httpClientNameTemp = $"{HttpClientName}_{api.Key}";
                        httpClient = httpClientFactory.CreateClient(httpClientNameTemp);
                        //var isA = CheckDomainAsync(api.Value, httpClientNameTemp, isHttps).Result;

                        ////检测是否可以访问域名
                        //if (isA)
                        //{
                        //}
                        break;
                    }
                    indexCurrent++;
                }
            }
        }

        #region 同步
        /// <summary>
        /// 同步发送请求方法
        /// </summary>
        /// <param name="method">请求方式</param>
        /// <param name="url">不包含（http/https + : + 域名 + /）,只要域名后面的，并且不含参数</param>
        /// <param name="parameters">请求参数</param>
        /// <param name="headers">请求头</param>
        /// <param name="contentType">参数的类容类型</param>
        /// <param name="timeout">请求超时时间（单位秒），默认为100秒</param>
        /// <param name="retryCount">重试次数，默认为2次</param>
        /// <returns></returns>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="HttpRequestException"></exception>
        public virtual HttpResponseMessage SendRequest(HttpMethod method, string url, Dictionary<string, object> parameters = null, Dictionary<string, string> headers = null, string contentType = "application/json", int timeout = 100, int retryCount = 2)
        {
            if (httpClient != null)
            {
                httpClient.Timeout = TimeSpan.FromSeconds(timeout);

                using (var request = new HttpRequestMessage(method, url))
                {
                    // 添加自定义 Header
                    if (headers != null)
                    {
                        foreach (var header in headers)
                        {
                            request.Headers.Add(header.Key, header.Value);
                        }
                    }

                    // 根据方法类型处理参数
                    if (method == HttpMethod.Get || method == HttpMethod.Delete)
                    {
                        // GET/DELETE：参数拼接到 URL
                        var uri = httpClient.BaseAddress;
                        if (uri != null)
                        {
                            request.RequestUri = BuildUrlWithQuery(uri.AbsoluteUri + url, parameters);
                        }
                    }
                    else
                    {
                        // 序列化请求体（支持 JSON/FormData）
                        if (parameters != null)
                        {
                            // POST/PUT/PATCH：参数写入请求体
                            switch (contentType)
                            {
                                case "application/json":
                                    request.Content = new StringContent(JsonHelper.SerializeObject(parameters), Encoding.UTF8, contentType);
                                    break;
                                case "application/x-www-form-urlencoded":
                                    request.Content = new FormUrlEncodedContent(ConvertDictionary(parameters));
                                    break;
                                default:
                                    throw new NotSupportedException($"不支持的媒体类型: {contentType}");
                            }
                        }
                    }

                    try
                    {
                        //using var response = httpClient.Send(request);

                        // 带重试机制的请求发送
                        var response = Policy
                            .Handle<HttpRequestException>()
                            .OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
                            .WaitAndRetry(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                            .Execute(() => httpClient.SendAsync(request).Result);

                        return response;
                    }
                    catch (HttpRequestException ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                    catch (Exception ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                }
            }
            else
            {
                throw new HttpRequestException($"请求失败：未创建HttpClient。");
            }
        }
        /// <summary>
        /// 同步上传文件方法
        /// </summary>
        /// <param name="apiUrl">不包含（http/https + : + 域名 + /）,只要域名后面的</param>
        /// <param name="formData">表单字段列表</param>
        /// <param name="fileDictionary">文件列表（HTTP内容的名称，文件完整路径名称）</param>
        /// <param name="headers">请求头</param>
        /// <param name="timeout">请求超时时间（单位秒），默认为100秒</param>
        /// <param name="retryCount">重试次数，默认为2次</param>
        /// <returns></returns>
        /// <exception cref="HttpRequestException"></exception>
        public virtual HttpResponseMessage UploadFiles(string apiUrl, Dictionary<string, string> formData = null, Dictionary<string, string> fileDictionary = null, Dictionary<string, string> headers = null, int timeout = 100, int retryCount = 2)
        {
            if (httpClient != null)
            {
                httpClient.Timeout = TimeSpan.FromSeconds(timeout);

                // 添加自定义 Header
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);//headers包含响应头
                    }
                }

                using (var multipartFormDataContent = new MultipartFormDataContent())
                {
                    if (formData != null)
                    {
                        foreach (var item in formData)
                        {
                            multipartFormDataContent.Add(new StringContent(item.Value), item.Key);
                        }
                    }

                    if (fileDictionary != null)
                    {
                        foreach (var file in fileDictionary)
                        {
                            // 添加文件参数
                            var fileStream = File.OpenRead(file.Value);
                            var streamContent = new StreamContent(fileStream);
                            // 可选：定义文件类型‌:ml-citation{ref="4" data="citationList"}
                            //streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");  
                            // 字段名file，文件名test.jpg‌:ml-citation{ref="1,4" data="citationList"}
                            // URL编码文件名‌:ml-citation{ref="4" data="citationList"}
                            multipartFormDataContent.Add(streamContent, file.Key, WebUtility.UrlEncode(Path.GetFileName(file.Value)));
                        }
                    }

                    try
                    {
                        // 发送请求
                        //using var response = httpClient.PostAsync(apiUrl, multipartFormDataContent).Result;

                        // 带重试机制的请求发送
                        var response = Policy
                            .Handle<HttpRequestException>()
                            .OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
                            .WaitAndRetry(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                            .Execute(() => httpClient.PostAsync(apiUrl, multipartFormDataContent).Result);

                        return response;
                    }
                    catch (HttpRequestException ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                    catch (Exception ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                }
            }
            else
            {
                throw new HttpRequestException($"请求失败：未创建HttpClient。");
            }
        }
        #endregion 同步

        #region 异步
        /// <summary>
        /// 异步发送请求方法
        /// </summary>
        /// <param name="method">请求方式</param>
        /// <param name="url">不包含（http/https + : + 域名 + /）,只要域名后面的，并且不含参数</param>
        /// <param name="parameters">请求参数</param>
        /// <param name="headers">请求头</param>
        /// <param name="contentType">参数的类容类型</param>
        /// <param name="timeout">请求超时时间（单位秒），默认为100秒</param>
        /// <param name="retryCount">重试次数，默认为2次</param>
        /// <returns></returns>
        /// <exception cref="NotSupportedException"></exception>
        /// <exception cref="HttpRequestException"></exception>
        public async Task<HttpResponseMessage> SendRequestAsync(HttpMethod method, string url, Dictionary<string, object> parameters = null, Dictionary<string, string> headers = null, string contentType = "application/json", int timeout = 100, int retryCount = 2)
        {
            if (httpClient != null)
            {
                httpClient.Timeout = TimeSpan.FromSeconds(timeout);

                using (var request = new HttpRequestMessage(method, url))
                {
                    // 添加自定义 Header
                    if (headers != null)
                    {
                        foreach (var header in headers)
                        {
                            request.Headers.Add(header.Key, header.Value);
                        }
                    }

                    // 根据方法类型处理参数
                    if (method == HttpMethod.Get || method == HttpMethod.Delete)
                    {
                        // GET/DELETE：参数拼接到 URL
                        var uri = httpClient.BaseAddress;
                        if (uri != null)
                        {
                            request.RequestUri = BuildUrlWithQuery(uri.AbsoluteUri + url, parameters);
                        }
                    }
                    else
                    {
                        // 序列化请求体（支持 JSON/FormData）
                        if (parameters != null)
                        {
                            // POST/PUT/PATCH：参数写入请求体
                            switch (contentType)
                            {
                                case "application/json":
                                    request.Content = new StringContent(JsonHelper.SerializeObject(parameters), Encoding.UTF8, contentType);
                                    break;
                                case "application/x-www-form-urlencoded":
                                    request.Content = new FormUrlEncodedContent(ConvertDictionary(parameters));
                                    break;
                                default:
                                    throw new NotSupportedException($"不支持的媒体类型: {contentType}");
                            }
                        }
                    }

                    try
                    {
                        //using var response = await httpClient.SendAsync(request);

                        // 带重试机制的请求发送
                        var response = await Policy
                            .Handle<HttpRequestException>()
                            .OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
                            .WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                            .ExecuteAsync(() => httpClient.SendAsync(request));

                        return response;
                    }
                    catch (HttpRequestException ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                    catch (Exception ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                }
            }
            else
            {
                throw new HttpRequestException($"请求失败：未创建HttpClient。");
            }
        }
        /// <summary>
        /// 异步上传文件方法
        /// </summary>
        /// <param name="apiUrl">不包含（http/https + : + 域名 + /）,只要域名后面的</param>
        /// <param name="formData">表单字段列表</param>
        /// <param name="fileDictionary">文件列表（HTTP内容的名称，文件完整路径名称）</param>
        /// <param name="headers">请求头</param>
        /// <param name="timeout">请求超时时间（单位秒），默认为100秒</param>
        /// <param name="retryCount">重试次数，默认为2次</param>
        /// <returns></returns>
        /// <exception cref="HttpRequestException"></exception>
        public async Task<HttpResponseMessage> UploadFilesAsync(string apiUrl, Dictionary<string, string> formData = null, Dictionary<string, string> fileDictionary = null, Dictionary<string, string> headers = null, int timeout = 100, int retryCount = 2)
        {
            if (httpClient != null)
            {
                httpClient.Timeout = TimeSpan.FromSeconds(timeout);

                // 添加自定义 Header
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);//headers包含响应头
                    }
                }

                using (var multipartFormDataContent = new MultipartFormDataContent())
                {
                    if (formData != null)
                    {
                        foreach (var item in formData)
                        {
                            multipartFormDataContent.Add(new StringContent(item.Value), item.Key);
                        }
                    }

                    if (fileDictionary != null)
                    {
                        foreach (var file in fileDictionary)
                        {
                            // 添加文件参数
                            var fileStream = File.OpenRead(file.Value);
                            var streamContent = new StreamContent(fileStream);
                            // 可选：定义文件类型‌:ml-citation{ref="4" data="citationList"}
                            //streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");  
                            // 字段名file，文件名test.jpg‌:ml-citation{ref="1,4" data="citationList"}
                            // URL编码文件名‌:ml-citation{ref="4" data="citationList"}
                            multipartFormDataContent.Add(streamContent, file.Key, WebUtility.UrlEncode(Path.GetFileName(file.Value)));
                        }
                    }

                    try
                    {
                        // 发送请求
                        //using var response = await httpClient.PostAsync(apiUrl, multipartFormDataContent);

                        // 带重试机制的请求发送
                        var response = await Policy
                            .Handle<HttpRequestException>()
                            .OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
                            .WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                            .ExecuteAsync(() => httpClient.PostAsync(apiUrl, multipartFormDataContent));

                        return response;
                    }
                    catch (HttpRequestException ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                    catch (Exception ex)
                    {
                        throw new HttpRequestException($"请求失败：{ex.ToString()}。");
                    }
                }
            }
            else
            {
                throw new HttpRequestException($"请求失败：未创建HttpClient。");
            }
        }
        #endregion 异步

        public Uri BuildUrlWithQuery(string baseUrl, Dictionary<string, object> parameters)
        {
            var uriBuilder = new UriBuilder(baseUrl);
            if (parameters != null)
            {
                var query = HttpUtility.ParseQueryString(uriBuilder.Query);
                foreach (var param in parameters)
                {
                    string value = "";
                    var obj = param.Value;
                    if (obj != null)
                    {
                        var valueStr = obj.ToString();
                        if (!string.IsNullOrWhiteSpace(valueStr))
                        {
                            value = valueStr;
                        }
                    }
                    query[param.Key] = Uri.EscapeDataString(value);
                }
                uriBuilder.Query = query.ToString();
            }
            return uriBuilder.Uri;
        }

        public Dictionary<string, string> ConvertDictionary(Dictionary<string, object> formData)
        {
            Dictionary<string, string> keys = new Dictionary<string, string>();
            if (formData != null)
            {
                foreach (var key in formData)
                {
                    string value = "";
                    var obj = key.Value;
                    if (obj != null)
                    {
                        var valueStr = obj.ToString();
                        if (!string.IsNullOrWhiteSpace(valueStr))
                        {
                            value = valueStr;
                        }
                    }
                    keys.Add(key.Key, value);
                }
            }
            return keys;
        }
    }
}
